package org.sws.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.alibaba.fastjson2.JSON;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;
import org.sws.domain.award.aggregate.AwardRecordTaskAggregate;
import org.sws.domain.award.aggregate.GiveOutPrizesAggregate;
import org.sws.domain.award.model.entity.AwardRecordEntity;
import org.sws.domain.award.model.entity.TaskEntity;
import org.sws.domain.award.model.entity.UserAwardRecordEntity;
import org.sws.domain.award.model.entity.UserCreditAwardEntity;
import org.sws.domain.award.model.valobj.AccountStatusVO;
import org.sws.domain.award.repository.IAwardRepository;
import org.sws.infrastructure.event.EventPublisher;
import org.sws.infrastructure.persistent.dao.*;
import org.sws.infrastructure.persistent.po.*;
import org.sws.infrastructure.redis.IRedisService;
import org.sws.types.common.Constants;
import org.sws.types.enums.ResponseCode;
import org.sws.types.enums.TaskState;
import org.sws.types.exception.AppException;

import javax.annotation.Resource;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @Author sws
 * @Date 2025/3/24 11:57
 * @description:
 */
@Slf4j
@Repository
public class AwardRepository implements IAwardRepository {

    @Resource
    private IUserAwardRecordDao userAwardRecordDao;
    @Resource
    private IAwardDao awardDao;
    @Resource
    private ITaskDao taskDao;
    @Resource
    private IUserRaffleOrderDao userRaffleOrderDao;
    @Resource
    private IUserCreditAccountDao userCreditAccountDao;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private IDBRouterStrategy dbRouter;
    @Resource
    private EventPublisher eventPublisher;
    @Resource
    private ThreadPoolExecutor executor;
    @Resource
    private IRedisService redisService;


    @Override
    public void createAwardRecord(AwardRecordTaskAggregate awardRecordTaskAggregate) {
        AwardRecordEntity userAwardRecordEntity = awardRecordTaskAggregate.getUserAwardRecordEntity();
        TaskEntity taskEntity = awardRecordTaskAggregate.getTaskEntity();
        String userId = userAwardRecordEntity.getUserId();
        Long activityId = userAwardRecordEntity.getActivityId();
        Integer awardId = userAwardRecordEntity.getAwardId();
        // 组装中奖记录流水po
        UserAwardRecord userAwardRecord = UserAwardRecord.builder()
                .userId(userId)
                .activityId(activityId)
                .strategyId(userAwardRecordEntity.getStrategyId())
                .orderId(userAwardRecordEntity.getOrderId())
                .awardId(awardId)
                .awardTitle(userAwardRecordEntity.getAwardTitle())
                .awardTime(userAwardRecordEntity.getAwardTime())
                .awardState(userAwardRecordEntity.getAwardState().getCode())
                .build();
        // 组装mq任务记录po
        Task task = Task.builder()
                .userId(taskEntity.getUserId())
                .topic(taskEntity.getTopic())
                .messageId(taskEntity.getMessageId())
                .message(JSON.toJSONString(taskEntity.getMessage()))
                .state(taskEntity.getState().getCode())
                .build();
        // 构建抽奖单对象
        UserRaffleOrder userRaffleOrderReq = UserRaffleOrder.builder()
                .userId(userId)
                .activityId(activityId)
                .orderId(userAwardRecordEntity.getOrderId())
                .build();
        try {
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    userAwardRecordDao.insert(userAwardRecord);
                    taskDao.insert(task);
                    // 更新抽奖单
                    int count = userRaffleOrderDao.updateUserRaffleOrderStateUsed(userRaffleOrderReq);
                    if (1 != count) {
                        status.setRollbackOnly();
                        log.error("写入中奖记录，用户抽奖单已使用过，不可重复抽奖 userId: {} activityId: {} awardId: {}", userId, activityId, awardId);
                        throw new AppException(ResponseCode.ACTIVITY_ORDER_ERROR.getCode(), ResponseCode.ACTIVITY_ORDER_ERROR.getInfo());
                    }
                    return 1;
                } catch (AppException e) {
                    status.setRollbackOnly();
                    log.error("写入中奖记录，唯一索引冲突 userId: {} activityId: {} awardId: {}",
                            userAwardRecord.getUserId(), userAwardRecord.getActivityId(), userAwardRecord.getAwardId(), e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                } catch (Exception e){
                    status.setRollbackOnly();
                    throw new RuntimeException(e);
                }
            });
        }finally {
            dbRouter.clear();
        }
        // 发布中奖MQ事件，异步执行奖品发放操作
        executor.execute(() ->{
            try {
                eventPublisher.publish(task.getTopic(), task.getMessage());
                // 更新任务表 记为完成
                task.setState(TaskState.complete.getCode());
                taskDao.updateTaskSendStatus(task);
            }catch (Exception e){
                log.error("发送中奖记录MQ事件异常 userId: {}, activityId: {}, awardId: {}",
                        userAwardRecord.getUserId(), userAwardRecord.getActivityId(), userAwardRecord.getAwardId(), e);
                // 更新任务表，记为失败
                task.setState(TaskState.fail.getCode());
                taskDao.updateTaskSendStatus(task);
            }
        });
    }

    @Override
    public List<AwardRecordEntity> queryAwardRecord(String userId, Long activityId) {
        return userAwardRecordDao.queryAwardRecord(userId, activityId);
    }

    @Override
    public String queryAwardConfig(Integer awardId) {
        return awardDao.queryAwardConfig(awardId);
    }

    @Override
    public void saveGiveOutPrizesAggregate(GiveOutPrizesAggregate giveOutPrizesAggregate) {
        log.info("奖品【随机积分】发放，userId{} raffleOrderId{}",giveOutPrizesAggregate.getUserId(),giveOutPrizesAggregate
                .getUserAwardRecordEntity().getRaffleOrderId());
        String userId = giveOutPrizesAggregate.getUserId();
        UserCreditAwardEntity userCreditAwardEntity = giveOutPrizesAggregate.getUserCreditAwardEntity();
        UserAwardRecordEntity userAwardRecordEntity = giveOutPrizesAggregate.getUserAwardRecordEntity();

        // 更新发奖记录状态为完成态
        UserAwardRecord userAwardRecordReq = new UserAwardRecord();
        userAwardRecordReq.setUserId(userId);
        userAwardRecordReq.setOrderId(userAwardRecordEntity.getRaffleOrderId());
        userAwardRecordReq.setAwardState(userAwardRecordEntity.getAwardState().getCode());

        // 更新用户积分 「首次则插入数据」
        UserCreditAccount userCreditAccountReq = new UserCreditAccount();
        userCreditAccountReq.setUserId(userCreditAwardEntity.getUserId());
        userCreditAccountReq.setTotalAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccountReq.setAvailableAmount(userCreditAwardEntity.getCreditAmount());
        userCreditAccountReq.setAccountStatus(AccountStatusVO.open.getCode());
        RLock lock = redisService.getLock(Constants.RedisKey.USER_CREDIT_RANDOM_LOCK + userId);
        try {
            lock.lock(3, TimeUnit.SECONDS);
            dbRouter.doRouter(giveOutPrizesAggregate.getUserId());
            transactionTemplate.execute(status -> {
                try {
                    // 更新积分 || 创建积分账户
                    UserCreditAccount userCreditAccountRes = userCreditAccountDao.queryUserCreditAccount(userCreditAccountReq);
                    if (null == userCreditAccountRes) {
                        userCreditAccountDao.insert(userCreditAccountReq);
                    } else {
                        userCreditAccountDao.updateAddAmount(userCreditAccountReq);
                    }

                    // 更新奖品记录
                    int updateAwardCount = userAwardRecordDao.updateAwardRecordCompletedState(userAwardRecordReq);
                    if (0 == updateAwardCount) {
                        log.warn("更新中奖记录，重复更新拦截 userId:{} giveOutPrizesAggregate:{}", userId, com.alibaba.fastjson.JSON.toJSONString(giveOutPrizesAggregate));
                        status.setRollbackOnly();
                    }
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("更新中奖记录，唯一索引冲突 userId: {} ", userId, e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
            lock.unlock();
        }
    }

    @Override
    public String queryAwardKey(Integer awardId) {
        Award award = awardDao.queryByAwardId(awardId);
        if(Objects.isNull(award)){
            return null;
        }
        return award.getAwardKey();
    }
}
